import { _decorator, Component, geometry, JsonAsset, Node, ParticleSystem, PhysicsSystem, Prefab, randomRange, randomRangeInt, tween, v3, Vec3 } from 'cc';
import { PoolManager } from 'db://assets/Scripts/Framework/Managers/PoolManager';
import Events from 'db://assets/Scripts/Framework/Utils/Events';
import { Builder } from '../../Navimesh/libs/Builder';
import { NavMesh } from '../../Navimesh/libs/NavMesh';
import { Bullet } from '../Bullet';
import { EnemyController } from '../EnemyController';
import { ItemBox } from '../ItemBox';
import { GamerController } from '../GamerController';
import { PoisonManager } from './PoisonManager';
import { RobotController } from '../RobotController';
const { ccclass, property } = _decorator;
const _vector1 = /*@__PURE__*/ new Vec3();
const _vector2 = /*@__PURE__*/ new Vec3();

@ccclass('InGameManager')
export class InGameManager extends Component {

    private static instance: InGameManager;
    public static get Instance(): InGameManager {
        return this.instance;
    }

    static itemsJson: any;
    // static bundle: AssetManager.Bundle;

    @property(Node)
    boxPos: Node;

    @property(Node)
    boxs: Node;

    @property(Node)
    bullets: Node;

    @property(Node)
    bloods: Node;

    @property(Node)
    cameraNode: Node;

    @property(Node)
    enemySpawn: Node;

    @property(Node)
    gamers: Node;

    @property(Node)
    player: Node;

    // @property(Node)
    // route: Node;

    // @property(Node)
    // plane: Node;

    @property(Node)
    controlUI: Node;

    @property(Node)
    effects: Node;

    @property(Prefab)
    itemBox: Prefab;

    @property(Prefab)
    bulletPrefab: Prefab;

    @property(Prefab)
    bloodPrefab: Prefab;

    @property(Prefab)
    fireworkPrefab: Prefab;

    @property(Prefab)
    enemyPrefab: Prefab;

    @property(JsonAsset)
    itemsJson: JsonAsset = null;

    @property(JsonAsset)
    navUrl: JsonAsset = null;

    @property([Prefab])
    gamerPrefabs: Prefab[] = [];

    private zone: string = null;

    allGamers = 30;
    aliveEnemy = 0;

    endData = [0, 0, 0, 0, 0, 0];

    protected onLoad(): void {
        InGameManager.instance = this;
        InGameManager.itemsJson = this.itemsJson.json;
        for (let i = 0; i < this.gamerPrefabs.length; i++) {
            const element = this.gamerPrefabs[i];
            PoolManager.ClearPool(element.name);
            let temp: Node = PoolManager.GetNodeByPrefab(element, this.gamers);
            temp.setWorldPosition(v3(0, -10000));
            this.scheduleOnce(() => { PoolManager.PutNode(temp); }, 1);
        }
        let adorns = InGameManager.itemsJson.Adorn;
        for (let i = 0; i < adorns.length; i++) {
            const element = adorns[i];
            for (let j = 0; j < element.length; j++) {
                const element2 = element[j];
                PoolManager.GetNodeByBundle("MainPack", element2.prefab, this.gamers, v3(0, -10000)).then((value) => { PoolManager.PutNode(value); });
            }
        }
        // this.InitGamers();
        // Events.on(EventName.JsonCompleted, this.InitBox, this);
    }

    start() {
        //解析json信息
        let zoneNodes = Builder.buildZoneByJson(this.navUrl.json);
        //区域名字直接使用json文件的名字
        this.zone = this.navUrl.name;
        NavMesh.setZoneData(this.zone, zoneNodes);
        this.InitBox();
        // this._InitPlane();
        // PoisonManager.Instance.MoveNextPoison();
        // this.ResetSpawn();
    }

    update(deltaTime: number) {

    }

    protected onDestroy(): void {
        Events.targetOff(this);
    }

    _InitPlane() {
        // this.route.children[2].active = true;
        // this.route.setRotationFromEuler(v3(0, randomRange(0, 360)));
        // this.plane.setPosition(v3(0, 400, 500));
        // tween(this.plane)
        //     .to(30, { position: v3(0, 400, -500) })
        //     .call(() => {
        //         let controlUI: any = this.controlUI.getComponent("ControlUI");
        //         if (!controlUI.joyRect.active) controlUI.Parachute();
        //         this.route.children[2].active = false;
        //     })
        //     .start();
    }

    InitBox() {
        // return;
        this.boxPos.children.forEach(element => {
            ItemBox.Init(element, true);
        });
    }

    InitGamers() {
        // return this.player.getComponent(GamerController).isInit = true;
        // let spawns = [...this.enemySpawn.children];
        // let pos = v3();
        // let num = randomRangeInt(0, this.enemySpawn.children.length);
        // let num = 14;
        // this.enemySpawn.children[num].getWorldPosition(pos);
        // this.player.setWorldPosition(pos);
        // spawns.splice(num, 1);
        // while (spawns.length > 10) {
        //     let num = randomRangeInt(0, spawns.length);
        //     let enemy: Node = PoolManager.GetNodeByPrefab(this.enemyPrefab, this.gamers);
        //     spawns[num].getWorldPosition(pos);
        //     enemy.setWorldPosition(pos);
        //     spawns.splice(num, 1);
        // }
        this.player.setParent(InGameManager.Instance.gamers, true);
        let player = this.player.getComponent(GamerController);
        player.characterController.enabled = true;
        this.scheduleOnce(() => {
            this.player.getComponent(GamerController).isInit = true;
            // for (let i = 0; i < this.gamers.children.length; i++) {
            //     const element = this.gamers.children[i];
            //     element.getComponent(GamerController).isInit = true;
            // }
        });
        for (let i = 0; i < 2; i++) {
            for (let i = 0; i < this.gamerPrefabs.length; i++) {
                const element = this.gamerPrefabs[i];
                let gamer = PoolManager.GetNodeByPrefab(element, this.gamers);
                PoolManager.PutNode(gamer);
            }
        }
    }

    ResetSpawn() {
        this.unschedule(this.SpawnEnemy);
        this.schedule(this.SpawnEnemy, Math.max(5 - PoisonManager.Instance.round, 1));
    }

    SpawnEnemy() {
        if (this.aliveEnemy < Math.min(2, this.allGamers - 1)) {
            this.aliveEnemy++;
            let spawns = [...this.enemySpawn.children];
            let playerPos = this.player.children[1].getWorldPosition();
            let pos = v3();
            spawns.sort((a, b) => { return Vec3.distance(playerPos, a.getWorldPosition(pos)) - Vec3.distance(playerPos, b.getWorldPosition(pos)); });
            for (let i = 0; i < spawns.length; i++) {
                const element = spawns[i];
                let ray = new geometry.Ray;
                element.getWorldPosition(pos)
                pos.y += 1;
                geometry.Ray.fromPoints(ray, playerPos, pos);
                if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0, Vec3.distance(pos, playerPos), false)) {
                    let enemy: Node = PoolManager.GetNodeByPrefab(this.enemyPrefab, this.gamers);
                    enemy.setWorldPosition(pos);
                    let src = enemy.getComponent("EnemyController") as any;
                    this.scheduleOnce(() => {
                        src.isInit = true;
                        src.Restart(this.player.getWorldPosition(pos));
                    })
                    break;
                }
            }
        }
    }

    Respawn(enemy: EnemyController) {
        let spawns = [...this.enemySpawn.children];
        let playerPos = this.player.children[1].getWorldPosition();
        let pos = v3();
        spawns.sort((a, b) => { return Vec3.distance(playerPos, a.getWorldPosition(pos)) - Vec3.distance(playerPos, b.getWorldPosition(pos)); });
        for (let i = 0; i < spawns.length; i++) {
            const element = spawns[i];
            let ray = new geometry.Ray;
            element.getWorldPosition(pos)
            pos.y += 1;
            geometry.Ray.fromPoints(ray, playerPos, pos);
            if (PhysicsSystem.instance.raycastClosest(ray, 1 << 0, Vec3.distance(pos, playerPos), false)) {
                enemy.isInit = false;
                enemy.node.setWorldPosition(pos);
                this.scheduleOnce(() => {
                    enemy.isInit = true;
                    enemy.StateSwitch(2, this.player.getWorldPosition());
                })
                break;
            }
        }
    }

    ShowTrail(startPos: Vec3, endPos: Vec3, prefab = this.bulletPrefab, call = () => { }) {
        let bullet: Node = PoolManager.GetNodeByPrefab(prefab, this.bullets);
        bullet.getComponent(Bullet).Init(startPos, endPos, call);
    }

    ShowBlood(pos: Vec3) {
        let blood: Node = PoolManager.GetNodeByPrefab(this.bloodPrefab, this.bullets);
        blood.setWorldPosition(pos);
        let particle = blood.getComponent(ParticleSystem);
        particle.play();
        this.scheduleOnce(() => { PoolManager.PutNode(blood) }, particle.duration + 0.5);
    }

    ShowFirework(pos: Vec3) {
        let blood: Node = PoolManager.GetNodeByPrefab(this.fireworkPrefab, this.bullets);
        blood.setWorldPosition(pos);
        let particle = blood.getComponent(ParticleSystem);
        particle.play();
        this.scheduleOnce(() => { PoolManager.PutNode(blood) }, particle.duration + 0.5);
    }

    /**
     * @Date: 2022-02-21 14:27:26
     * @LastEditors: iwae
     * @description: 寻找路径
     * @param {string} zoneID 区域ID，这里我们使用的json的文件名作为区域ID
     * @param {Vec3} pos 当前Agent的位置，父节点在中心点，我们使用了local postion
     * @param {Vec3} des 目的地的坐标
     */
    findPath(zoneID: string, pos: Vec3, des: Vec3): Vec3[] {
        _vector1.set(pos);
        const groupId = NavMesh.getGroup(zoneID, _vector1);
        _vector2.set(des);
        const path = NavMesh.findPath(_vector1, _vector2, zoneID, groupId);
        return path
    }

    /**
     * @Date: 2022-02-21 14:28:56
     * @LastEditors: iwae
     * @description: 
     * @param {NavMeshAgent} agent 传入Agent脚本
     * @param {Vec3} des 目的地位置
     */
    moveAgent(agent: EnemyController | RobotController, des: Vec3, zone = this.zone): boolean {
        let path = this.findPath(zone, agent.node.position, des)
        if (path && path.length > 0) {
            agent.startMove(path);
            return true;
        }
        else {
            agent.stopMove();
            return false;
        }
    }

}


